/*
 * author      : 吴中勤
 * date        : 2018/8/16
 * description : 负责维护 (moby-SAP) order 之间的关系
 */

package com.deepexi.adapter.converter.sap.order;

import com.deepexi.adapter.common.DateUtils;
import com.deepexi.adapter.common.StringUtil;
import com.deepexi.adapter.common.exception.HttpNon200Exception;
import com.deepexi.adapter.common.sdk.RequestInfo;
import com.deepexi.adapter.config.LoggerManage;
import com.deepexi.adapter.converter.sap.MobyEventBodyPreProcessor;
import com.deepexi.adapter.converter.sap.SapConverterContext;
import com.deepexi.adapter.converter.sap.SapMapConst;
import com.deepexi.adapter.converter.sap.SapSdkIntercepter;
import com.deepexi.adapter.facade.moby.request.MobyEventBody;
import com.deepexi.adapter.facade.moby.request.MobyOrderLine;
import com.deepexi.adapter.facade.moby.request.MobyOrderLineTreeNode;
import com.deepexi.adapter.sdk.sap.SapRecords;
import com.deepexi.adapter.sdk.sap.SapSdk;
import com.deepexi.adapter.sdk.sap.dto.order.SapOrderLine;
import com.deepexi.adapter.sdk.sap.dto.order.SapOrderLineLite;
import com.deepexi.adapter.sdk.sap.dto.order.SapOrderRequestBody;
import com.deepexi.adapter.sdk.sap.dto.order.SapOrderResponseBody;
import com.deepexi.adapter.sdk.sap.exception.SapBizException;
import org.json.JSONException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
public class OrderService {

    private static final Logger logger = LoggerFactory.getLogger(OrderService.class);

    @Autowired
    private SapSdk sapSdk;

    @Autowired
    private SapAdapterOrderMapper orderMapper;

    @Autowired
    private SapAdapterOrderLineMapper lineMapper;

    @Autowired
    private OrderAssembler orderAssembler;

    @Autowired
    private SapConverterContext sapConverterContext;

    @Autowired
    private SapSdkIntercepter sapSdkIntercepter;

    @Autowired
    private MobyEventBodyPreProcessor preProcessor;

    /**
     * 创建SAP订单（从 order 和 lineList 中, 组合数据）
     * 如果是新车, 则将 新车 + 分期信息放在一起
     * - - 如果有定金，则在订单中带上定金信息(此接口的定金信息 不用再去 预付款申请并清除)
     * <p>
     * moby_order_id - SAP_order_id - moby_line_id - SAP_line_id
     *
     * @param body
     * @return
     */
    @LoggerManage(description = "创建订单")
    public SapOrderResponseBody createSapOrder(MobyEventBody body) throws IOException, JSONException, SapBizException, HttpNon200Exception {
        if (null == body) throw new SapBizException("创建订单, 但是输入参数为空");
        preProcessor.preProcess(body);

        /* moby请求对象 -> sap请求对象 */
        SapOrderRequestBody requestBody = orderAssembler.assembleCreate(body);

        /* 发送请求并得到结果 */
        SapOrderResponseBody responseBody = orderApi(requestBody);

        /* 处理结果 */
        if (!responseBody.isOk()) {
            String responseCode = responseBody.getCode();
            /* 分车失败、自动采购失败, 不影响订单 */
            if (SapOrderResponseBody.ERROR_CODE_AUTO_PURCHASE.equals(responseCode)) {
                saveOrderRelationship(body, responseBody, requestBody);
                throw new SapBizException("sap下单,自动采购出错, 不影响下订单, 已经保存");
            } else if (SapOrderResponseBody.ERROR_CODE_LOCK_VEHICLE.equals(responseCode)) {
                saveOrderRelationship(body, responseBody, requestBody);
                clearVehicleCode(body);
                throw new SapBizException("sap下单,分车失败, 需要重新分车");
            } else {
                throw new SapBizException("sap下单出错, 返回结果:" + responseBody.toString());
            }
        }
        saveOrderRelationship(body, responseBody, requestBody);
        return responseBody;
    }

    @LoggerManage(description = "订单分车")
    public void onVehicleLocked(MobyEventBody body) throws IOException, JSONException, SapBizException, HttpNon200Exception {
        if (null == body) throw new SapBizException("进行分车, 但是输入参数为空");
        preProcessor.preProcess(body);

        SapOrderRequestBody requestBody = orderAssembler.assembleLockVehicle(body);
        /* 发送请求并得到结果 */
        SapOrderResponseBody responseBody = orderApi(requestBody);
        /* 处理结果 */
        if (!responseBody.isOk()) {
            clearVehicleCode(body);
            throw new SapBizException("sap分车出错, 返回结果:" + responseBody.toString());
        }
        /* 更新分车信息 */
        SapAdapterOrder oldRecord = orderMapper.findOneByMobyOrder(body.getMobyTradeOrder().getId());
        oldRecord.setVechileCode(requestBody.getVechileCode());
        orderMapper.updateByMobyOrder(oldRecord);
    }

    @LoggerManage(description = "取消分车")
    public void onVehicleUnlocked(MobyEventBody body) throws IOException, JSONException, SapBizException, HttpNon200Exception {
        preProcessor.preProcess(body);

        if (null == body) throw new SapBizException("取消分车, 但是输入参数为空");
        SapOrderRequestBody requestBody = orderAssembler.assembleUnlockVehicle(body);
        /* 发送请求并得到结果 */
        SapOrderResponseBody responseBody = orderApi(requestBody);
        /* 处理结果 */
        if (!responseBody.isOk())
            throw new SapBizException("sap取消分车出错, 返回结果:" + responseBody.toString());
        SapAdapterOrder oldRecord = orderMapper.findOneByMobyOrder(body.getMobyTradeOrder().getId());
        oldRecord.setVechileCode("");
        orderMapper.updateByMobyOrder(oldRecord);
    }

    @LoggerManage(description = "取消分车")
    public void onVehicleUnlockedById(String mobyTradeOrderId) throws IOException, JSONException, SapBizException, HttpNon200Exception {
        // preProcess(body);

        if (null == mobyTradeOrderId) throw new SapBizException("取消分车, 但是输入参数为空");
        SapOrderRequestBody requestBody = orderAssembler.assembleUnlockVehicleById(mobyTradeOrderId);
        /* 发送请求并得到结果 */
        SapOrderResponseBody responseBody = orderApi(requestBody);
        /* 处理结果 */
        if (!responseBody.isOk())
            throw new SapBizException("sap取消分车出错, 返回结果:" + responseBody.toString());
        SapAdapterOrder oldRecord = orderMapper.findOneByMobyOrder(mobyTradeOrderId);
        oldRecord.setVechileCode("");
        orderMapper.updateByMobyOrder(oldRecord);
    }

    @LoggerManage(description = "金融方案确定")
    public void onLoanConfirmed(MobyEventBody body) throws IOException, JSONException, SapBizException, HttpNon200Exception {
        if (null == body) throw new SapBizException("确认金融方案, 但是输入参数为空");
        preProcessor.preProcess(body);

        SapOrderRequestBody requestBody = orderAssembler.assembleLoan(body);
        /* 发送请求并得到结果 */
        SapOrderResponseBody responseBody = orderApi(requestBody);
        /* 处理结果 */
        if (!responseBody.isOk())
            throw new SapBizException("sap 确认金融方案出错, 返回结果:" + responseBody.toString());
        // 录入金融订单行, 修改订单信息
        saveOrderRelationship(body, responseBody, requestBody);
    }

    @LoggerManage(description = "保险方案确认")
    public void onInsuranceConfirmed(MobyEventBody body) throws IOException, JSONException, SapBizException, HttpNon200Exception {
        if (null == body) throw new SapBizException("确认保险方案, 但是输入参数为空");
        preProcessor.preProcess(body);

        SapOrderRequestBody requestBody = orderAssembler.assembleInsurance(body);
        /* 发送请求并得到结果 */
        SapOrderResponseBody responseBody = orderApi(requestBody);
        /* 处理结果 */
        if (!responseBody.isOk())
            throw new SapBizException("sap 确认保险方案, 返回结果:" + responseBody.toString());
        /* 录入保险订单行, 订单信息不用动 */
        saveOrderRelationship(body, responseBody, requestBody);
    }

    @LoggerManage(description = "提供精品保养")
    public void onOfferBoutiqueOrMaintain(MobyEventBody body) throws IOException, JSONException, SapBizException, HttpNon200Exception {
        if (null == body) throw new SapBizException("提供精品或保险, 但是输入参数为空");
        preProcessor.preProcess(body);
        SapOrderRequestBody requestBody = orderAssembler.assembleOfferBoutiqueOrMaintain(body);

        /* 发送请求并得到结果 */
        SapOrderResponseBody responseBody = orderApi(requestBody);

        /* 处理结果 */
        if (!responseBody.isOk())
            throw new SapBizException("sap 提供精品保养, 返回结果:" + responseBody.toString());
        
        /* 保存同步的订单行 */
        saveOrderRelationship(body, responseBody, requestBody);

        /* TODO 保存 劫持下来的保养信息 */
        saveMaintainLine(body);
    }

    @LoggerManage(description = "修改顾问")
    public void onConsultChanged(MobyEventBody body) throws IOException, JSONException, SapBizException, HttpNon200Exception {
        if (null == body) throw new SapBizException("修改顾问, 但是输入参数为空");
        preProcessor.preProcess(body);

        SapOrderRequestBody requestBody = orderAssembler.assembleConsultChanged(body);
        /* 发送请求并得到结果 */
        SapOrderResponseBody responseBody = orderApi(requestBody);
        /* 处理结果 */
        if (!responseBody.isOk())
            throw new SapBizException("sap 修改顾问, 返回结果:" + responseBody.toString());

        /* 修改销售顾问 */
        SapAdapterOrder oldRecord = orderMapper.findOneByMobyOrder(body.getMobyTradeOrder().getId());
        oldRecord.setScCode(body.getMobyTradeOrder().getScCode());
        orderMapper.updateByMobyOrder(oldRecord);
    }

    /** 精品的修改, 包含增加和删除 不包含修改 */
    @LoggerManage(description = "增删精品")
    public void onBoutiqueChanged(MobyEventBody body) throws IOException, JSONException, SapBizException, HttpNon200Exception {
        if (null == body) throw new SapBizException("增删精品, 但是输入参数为空");
        preProcessor.preProcess(body);

        /* 找出新增 和 删除的 订单行, 进行处理 */
        SapOrderRequestBody requestBody = orderAssembler.assembleBoutiqueChanged(body);
        /* 发送请求并得到结果 */
        SapOrderResponseBody responseBody = orderApi(requestBody);
        /* 处理结果 */
        if (!responseBody.isOk())
            throw new SapBizException("sap 增删精品, 返回结果:" + responseBody.toString());

        // TODO 保存信息
        saveOrderRelationship(body, responseBody, requestBody);
    }

    @LoggerManage(description = "billing")
    public void onBilling(MobyEventBody body) throws IOException, JSONException, SapBizException, HttpNon200Exception {
        if (null == body) throw new SapBizException("billing, 但是输入参数为空");
        preProcessor.preProcess(body);

        /*  找到保存的 order, 找到对应的付款记录, 组装 */
        SapOrderRequestBody requestBody = orderAssembler.assembleBilling(body);
        /* 发送请求并得到结果 */
        SapOrderResponseBody responseBody = orderApi(requestBody);
        /* 处理结果 */
        if (!responseBody.isOk())
            throw new SapBizException("创建结算通知单异常, 返回结果:" + responseBody.toString());

        /* 保存信息 */
        SapAdapterOrder oldRecord = orderMapper.findOneByMobyOrder(body.getMobyTradeOrder().getId());
        oldRecord.billed();
        orderMapper.updateByMobyOrder(oldRecord);
    }

    @LoggerManage(description = "物料采购")
    public void onPurchaseItem(String mobyOrderId, String mobyLineId) throws IOException, JSONException, SapBizException, HttpNon200Exception {
        if (null == mobyOrderId || null == mobyLineId) throw new SapBizException("物料采购, 但是输入参数为空");
        // 找到保存的 order, 找到对应的付款记录, 组装,
        SapOrderRequestBody requestBody = orderAssembler.assemblePurchaseItem(mobyOrderId, mobyLineId);
        SapOrderResponseBody responseBody = orderApi(requestBody);
        /* 处理结果 */
        if (!responseBody.isOk())
            throw new SapBizException("创建结算通知单异常, 返回结果:" + responseBody.toString());
        /* 不需要保存 */
    }

    @LoggerManage(description = "精品出库")
    public void onBoutiqueStockOut(MobyEventBody body) throws IOException, JSONException, SapBizException, HttpNon200Exception {
        if (null == body) throw new SapBizException("精品出库, 但是输入参数为空");
        preProcessor.preProcess(body);

        onItemStockout(body);
    }

    @LoggerManage(description = "车辆出库")
    public void onVehicleStockOut(MobyEventBody body) throws IOException, JSONException, SapBizException, HttpNon200Exception {
        if (null == body) throw new SapBizException("车辆出库, 但是输入参数为空");
        preProcessor.preProcess(body);

        onItemStockout(body);
    }

    private void onItemStockout(MobyEventBody body) throws IOException, JSONException, SapBizException, HttpNon200Exception {
        SapOrderRequestBody requestBody = orderAssembler.assembleStockOut(body);
        /* 发送请求并得到结果 */
        SapOrderResponseBody responseBody = orderApi(requestBody);
        /* 处理结果 */
        if (!responseBody.isOk())
            throw new SapBizException("出库异常, 返回结果:" + responseBody.toString());

        /* 保存已出库信息 */
        List<String> stockOutList = requestBody.getLineList()
                .stream()
                .map(SapOrderLine::getSapLineNo)
                .collect(Collectors.toList());

        lineMapper.batchUpdateStockOut(
                body.getMobyTradeOrder().getId(),
                stockOutList,
                SapMapConst.ORDER_LINE_STATUS_STOCK_OUT);
    }

    /**
     * TODO throw DAOException
     * 保存保养订单的详情
     */
    private void saveMaintainLine(MobyEventBody body) {
        List<MobyOrderLineTreeNode> maintainList = body.getRetainLineList();
        if (null == maintainList || 0 == maintainList.size()) return;
        List<SapAdapterOrderLine> toSaveLineList = new ArrayList<>(maintainList.size());

        maintainList.forEach(treeNode -> {
            SapAdapterOrderLine line = new SapAdapterOrderLine();
            line.setMobyOrderId(treeNode.getTradeOrderId());
            line.setMobyLineId(treeNode.getId());
            line.setSapLineNo("");
            line.setProductType(treeNode.getProductType());
            line.setItCat("");
            line.setStatus("");
            line.setMaterialNo(treeNode.getMaterialCode());
            line.setPrice(treeNode.getPriceMoney());
            line.setQuantity(treeNode.getQuantity());
            line.setSyncType(SapMapConst.ORDER_LINE_TYPE_RETAIN);
            toSaveLineList.add(line);
        });

        lineMapper.batchInsert(toSaveLineList);
    }

    /** 生成 新增订单关系 */
    private SapAdapterOrder generateSapAdapterOrder(SapOrderResponseBody result, SapOrderRequestBody requestBody) {
        SapAdapterOrder orderRelation = new SapAdapterOrder();
        BeanUtils.copyProperties(requestBody, orderRelation);

        orderRelation.setSapOrderNo(result.getSapOrderNo());
        orderRelation.setBpNum(sapConverterContext.getMemberBP().getBpNum());
        String now = DateUtils.nowDateTime();
        orderRelation.setCreatedAt(now);
        orderRelation.setUpdatedAt(now);

        return orderRelation;
    }

    /** 生成 orderline之间的对应关系 */
    private SapAdapterOrderLine generatorSapAdapterOrderLine(MobyOrderLine mobyLine, SapOrderLineLite sapLine, SapOrderLine sapOrderLine, String orderId) {
        SapAdapterOrderLine lineRelation = new SapAdapterOrderLine();
        lineRelation.setMobyOrderId(orderId);
        lineRelation.setMobyLineId(mobyLine.getId());
        lineRelation.setSapLineNo(sapLine.getSapLineNo());
        lineRelation.setProductType(mobyLine.getProductType());

        lineRelation.setItCat(sapOrderLine.getItemCatalog());
        lineRelation.setMaterialNo(sapOrderLine.getMaterialNo());
        lineRelation.setQuantity(mobyLine.getQuantity());
        /* sync type */
        String syncType = mobyLine.getSyncType();
        syncType = StringUtil.isNullorEmpty(syncType) ? SapMapConst.ORDER_LINE_TYPE_SYNCED : syncType;
        lineRelation.setSyncType(syncType);
        /* moby 单价 */
        lineRelation.setPrice(mobyLine.getPriceMoney());
        /* sap 单价 */
        lineRelation.setPayPrice(sapOrderLine.getPrice());

        return lineRelation;
    }

    /**
     * 保存 2个系统之间的 订单映射关系
     * TODO throw DAOException
     *
     * @param body         原始请求报文
     * @param responseBody sap返回结果
     * @param requestBody  发送给sap的请求
     */
    private void saveOrderRelationship(MobyEventBody body, SapOrderResponseBody responseBody, SapOrderRequestBody requestBody) {
        /* 根据 mobyLine.id 关联 moby订单行-sap订单行 */
        String mobyTradeOrderId = body.getMobyTradeOrder().getId();

        /* 处理 订单行映射 */
        List<SapOrderLineLite> resultLineList = responseBody.getLineLiteList();
        if (null != resultLineList && 0 != resultLineList.size()) {

            /* moby订单行 列表*/
            List<MobyOrderLine> mobyOrderLineList = body.getFlatTradeOrderLineList();

            /* 新增的订单行 */
            List<SapAdapterOrderLine> toSaveLineList = new ArrayList<>(mobyOrderLineList.size());

            /* 删除的订单行 */
            List<String> toDeleteLineList = new ArrayList<>(mobyOrderLineList.size());              //

            Map<String, MobyOrderLine> mobyLineMap = new HashMap<>();
            mobyOrderLineList.forEach(mobyLine -> mobyLineMap.put(mobyLine.getId(), mobyLine));
            Map<String, SapOrderLine> requestLineMap = new HashMap<>();
            requestBody.getLineList().forEach(sapOrderLine -> requestLineMap.put(sapOrderLine.getExternalLineNo(), sapOrderLine));

            resultLineList.forEach(sapLine -> {
                SapOrderLine sapOrderLine = requestLineMap.get(sapLine.getExternalLineNo());
                if (sapOrderLine.isChangeTypeAdd()) {
                    MobyOrderLine mobyLine = mobyLineMap.get(sapLine.getExternalLineNo());
                    SapAdapterOrderLine lineRelation = generatorSapAdapterOrderLine(mobyLine, sapLine, sapOrderLine, mobyTradeOrderId);
                    toSaveLineList.add(lineRelation);
                } else if (sapOrderLine.isChangeTypeDelete()) {
                    toDeleteLineList.add(sapOrderLine.getExternalLineNo());
                } else {
                    // do nothing
                }
            });

            /* 遍历完毕, 入库 */
            if (toSaveLineList.size() > 0) lineMapper.batchInsert(toSaveLineList);
            if (toDeleteLineList.size() > 0) lineMapper.batchDelete(toDeleteLineList);
        }

        /* 处理订单映射 */
        SapAdapterOrder oldRecord = orderMapper.findOneByMobyOrder(mobyTradeOrderId);
        String sapHint = responseBody.isOk() ? "" : responseBody.getMsg();
        if (null == oldRecord) {
            /* 新增订单 */
            SapAdapterOrder orderRelation = generateSapAdapterOrder(responseBody, requestBody);
            orderRelation.setSapHint(sapHint);
            orderMapper.insert(orderRelation);
        } else {
            /* 更新订单 */
            BeanUtils.copyProperties(responseBody, oldRecord);
            oldRecord.setSapHint(sapHint);
            orderMapper.updateByMobyOrder(oldRecord);
        }
    }

    /* 分车失败, 需要清理订单的 vehicleCode */
    private void clearVehicleCode(MobyEventBody body) {
        orderMapper.clearVehicleCode(body.getMobyTradeOrder().getId());
    }

    private SapOrderResponseBody orderApi(SapOrderRequestBody requestBody)
            throws IOException, JSONException, SapBizException, HttpNon200Exception {
        /* 发送请求并得到结果 */
        RequestInfo requestInfo = sapSdk.requestInfo4Order(new SapRecords<>(requestBody));
        String respBodyStr = sapSdkIntercepter.doHttpAndLogWithRetryIfFail(requestInfo);
        /* 日志和重试 */
        return sapSdk.parseResult(requestInfo, respBodyStr);
    }

}
